package tx.screens;

import java.awt.AlphaComposite;
import java.awt.Composite;
import java.awt.Container;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Insets;
import java.awt.Transparency;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.io.File;
import java.net.URL;

import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;

import tx.core.BlockColors;
import tx.core.Blocks;
import tx.core.Tetraminoe;
import tx.core.ZTetraminoe;
import tx.core.input.GameAction;
import tx.core.input.InputManager;
import tx.development.Development;
import tx.ui.ScreenManager;

/**
 * The Game Screen. It will show the game de facto. By game screen it means
 * the screen where the board, next tetramoninoe, the current score and
 * buttons for pausing and quiting the game.
 * 
 * @author Guilherme Mauro Germoglio Barbosa - germoglio@gmail.com
 */
public class GameScreen implements ActionListener, Screen {

	private Blocks blocks = new Blocks();
	
	/**
	 * Tool tip for exit button.
	 */
	private static final String EXIT_BUTTON_TOOLTIP = "exit";

	/**
	 * Tool tip for pause button.
	 */
	private static final String PAUSE_BUTTON_TOOLTIP = "pause";

	/**
	 * Tool tip for play button.
	 */
	private static final String PLAY_BUTTON_TOOLTIP = "play";

	/**
	 * Tool tip for exit button.
	 */
	private static final String EXIT_BUTTON_FILE_NAME = "x-exit";

	/**
	 * Pause button base name.
	 */
	private static final String PAUSE_BUTTON_FILE_NAME = "ii-pause";

	/**
	 * Play button base name.
	 */
	private static final String PLAY_BUTTON_FILE_NAME = "v-play";

	/**
	 * Buttons file name sufix.
	 */
	private static final String BUTTON_FILE_NAME_SUFIX = ".png";

	/**
	 * Buttons file name prefix.
	 */
	private static final String BUTTON_FILE_NAME_PREFIX = "images" + File.separator + "buttons" + File.separator;

	/**
	 * Background images file name sufix.
	 */
	private static final String LEVEL_BG_IMAGE_SUFIX = ".jpg";

	/**
	 * Background images file name prefix.
	 */
	private static final String LEVEL_BG_IMAGE_PREFIX = "images" + File.separator + "levels" + File.separator + "background-";

	/**
	 * The number of background images available.
	 */
	private static final int NUMBER_OF_BG_IMAGES = 69;

	/**
	 * The background image.
	 */
	private Image currentBGImage;

	/**
	 * The GameAction for play.
	 */
	private GameAction playGameAction;
	/**
	 * The GameAction for pause.
	 */
	private GameAction pauseGameAction;
	/**
	 * The GameAction for exit.
	 */
	private GameAction exitGameAction;
	/**
	 * The GameAction for down.
	 */
	private GameAction downGameAction;
	/**
	 * The GameAction for left.
	 */
	private GameAction leftGameAction;
	/**
	 * The GameAction for right.
	 */
	private GameAction rightGameAction;
	
	/**
	 * Game action for rotating the tetraminoe.
	 */
	private GameAction rotateGameAction;
	
	// FIXME just for debugging!
	private GameAction nextLevel;

	/**
	 * The ScreenManager used.
	 */
	private ScreenManager screenManager;

	/**
	 * The InputManager used.
	 */
	private InputManager inputManager;
	/**
	 * The play button.
	 */
	private JButton playButton;
	/**
	 * The pause button.
	 */
	private JButton pauseButton;
	/**
	 * The exit button. 
	 */
	private JButton exitButton;

	/**
	 * The space where play and pause button will be placed.
	 */
	private JPanel playPauseButtonSpace;

	/**
	 * If it is running or not.
	 */
	private boolean isRunning;

	/**
	 * The current level.
	 */
	private int currentLevel;

	/**
	 * The next screen it should be displayed.
	 */
	private Screen nextScreen;

	/**
	 * The TitleScreen that should be the next screen when exit was pressed.
	 */
	private TitleScreen titleScreen;

	/**
	 * If the game is paused or not.
	 */
	private boolean isPaused;
	
	private Tetraminoe block;
	
	/**
	 * Creates a GameScreen, using the specified ScreenManager.
	 * @param screenManager The ScreenManager.
	 */
	public GameScreen(ScreenManager screenManager) {
		this.screenManager = screenManager;
	}
	
	/**
	 * Sets the title screen.
	 * @param titleScreen The TitleScreen.
	 */
	public void setTitleScreen(TitleScreen titleScreen) {
		this.titleScreen = titleScreen;
	}
	
	/**
	 * Inits this screen.
	 */
	public void init() {
		
		this.isRunning = true;
		
		this.nextScreen = this;
		
		this.currentLevel = 1;
		
		this.isPaused = false;
		
		initInputManager();
		
		initButtons();
		
		initScreen();
		
		block = new ZTetraminoe(BlockColors.ORANGE);
		block.setVelocityY(.05f);
		block.setX(30);
		block.setY(30);
		
	}
	
	
	/**
	 * Inits the screen, putting buttons in place and setting the first background image.
	 */
	private void initScreen() {
		JFrame frame = screenManager.getFullScreenWindow();
		
		Container contentPane = frame.getContentPane();
		
		if (contentPane instanceof JComponent) {
			((JComponent)contentPane).setOpaque(false);
		}
		
		contentPane.setLayout(null);
		
		playPauseButtonSpace = new JPanel();
        playPauseButtonSpace.setOpaque(false);
		
		Insets insets = contentPane.getInsets();
		
		playPauseButtonSpace.add(pauseButton);
		
		contentPane.add(playPauseButtonSpace);
		contentPane.add(exitButton);
		
		Dimension playButtonSize = playButton.getPreferredSize();
		int xPlayButton = 920 + insets.left;
		int yPlayButton = 24 + insets.top;
		playButton.setBounds(xPlayButton, yPlayButton, playButtonSize.width, playButtonSize.height);
		
		Dimension pauseButtonSize = pauseButton.getPreferredSize();
		int xPauseButton = 920 + insets.left;
		int yPauseButton = 24 + insets.top;
		pauseButton.setBounds(xPauseButton, yPauseButton, pauseButtonSize.width, pauseButtonSize.height);
		
		Dimension exitButtonSize = exitButton.getPreferredSize();
		int xExitButton = 950 + insets.left;
		int yExitButton = 24 + insets.top;
		exitButton.setBounds(xExitButton, yExitButton, exitButtonSize.width, exitButtonSize.height);
		
		Dimension playPauseButtonSize = playPauseButtonSpace.getPreferredSize();
		int xPlayPauseSpace = 905 + insets.left;
		int yPlayPauseSpace = 19 + insets.top;
		playPauseButtonSpace.setBounds(xPlayPauseSpace, yPlayPauseSpace, playPauseButtonSize.width, playPauseButtonSize.height);
		
		playPauseButtonSpace.validate();
		frame.validate();

		this.currentBGImage = getBackgroundImage(currentLevel); 
		
	}

	/**
	 * Returns a background image for the corresponding level.
	 * @param level The level.
	 * @return A background image for the level.
	 */
	private Image getBackgroundImage(int level) {
		if (Development.DEBUG_ENABLED) System.out.println(Development.DEBUG_LABEL + "GameScreen.getBackgroundImage() " + LEVEL_BG_IMAGE_PREFIX + (level % NUMBER_OF_BG_IMAGES) + LEVEL_BG_IMAGE_SUFIX); 
		return new ImageIcon(this.getClass().getResource(LEVEL_BG_IMAGE_PREFIX + (level % NUMBER_OF_BG_IMAGES) + LEVEL_BG_IMAGE_SUFIX)).getImage();
	}

	/**
	 * Inits the Buttons on the screen.
	 */
	private void initButtons() {
		this.playButton = createButton(BUTTON_FILE_NAME_PREFIX + PLAY_BUTTON_FILE_NAME, PLAY_BUTTON_TOOLTIP);
		this.pauseButton = createButton(BUTTON_FILE_NAME_PREFIX + PAUSE_BUTTON_FILE_NAME, PAUSE_BUTTON_TOOLTIP);
		this.exitButton = createButton(BUTTON_FILE_NAME_PREFIX + EXIT_BUTTON_FILE_NAME, EXIT_BUTTON_TOOLTIP);
	}

	/**
	 * Creates a button for the title screen.
	 * @param imageName The image name of the button. It must be the base name, since the image must always
	 * be a PNG. It must be also reachable through TitleScreen.class.getResource(String) method.
	 * @param tooltip The button tip message.
	 * FIXME: refactor (GameScreen.createButton(String) and TitleScreen.createButton(String) are the same).
	 *   
	 * @return A JButton.
	 */
	private JButton createButton(String imageName, String tooltip) {
		URL imagePath = this.getClass().getResource(imageName + BUTTON_FILE_NAME_SUFIX);
		ImageIcon iconRollover = new ImageIcon(imagePath);
		int w = iconRollover.getIconWidth();
		int h = iconRollover.getIconHeight();
		
		Cursor cursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR);
		
		Image image = screenManager.createCompatibleImage(w, h, Transparency.TRANSLUCENT);
		Graphics2D g = (Graphics2D)image.getGraphics();
		Composite alpha = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, .5f);
		g.setComposite(alpha);
		g.drawImage(iconRollover.getImage(), 0, 0, null);
		g.dispose();
		ImageIcon iconDefault = new ImageIcon(image);
		
		image = screenManager.createCompatibleImage(w, h, Transparency.TRANSLUCENT);
		g = (Graphics2D)image.getGraphics();
		g.drawImage(iconRollover.getImage(), 2, 2, null);
		g.dispose();
		ImageIcon iconPressed = new ImageIcon(image);
		
		JButton button = new JButton();
		button.addActionListener(this);
		button.setIgnoreRepaint(true);
		button.setFocusable(false);
		button.setBorder(null);
		button.setContentAreaFilled(false);
		button.setCursor(cursor);
		button.setIcon(iconDefault);
		button.setRolloverIcon(iconRollover);
		button.setPressedIcon(iconPressed);
		button.setToolTipText(tooltip);
		
		return button;
	}
	
	/**
	 * Inits the InputManager, configuring the GameActions.
	 */
	private void initInputManager() {
		this.inputManager = new InputManager(this.screenManager.getFullScreenWindow());
		
		this.playGameAction = new GameAction(PLAY_BUTTON_TOOLTIP, GameAction.DETECT_INITIAL_PRESS_ONLY);
		this.pauseGameAction = new GameAction(PAUSE_BUTTON_TOOLTIP, GameAction.DETECT_INITIAL_PRESS_ONLY);
		this.exitGameAction = new GameAction(EXIT_BUTTON_TOOLTIP, GameAction.DETECT_INITIAL_PRESS_ONLY);
		this.leftGameAction = new GameAction("move left");
		this.rightGameAction = new GameAction("move right");
		this.rotateGameAction = new GameAction("rotate", GameAction.DETECT_INITIAL_PRESS_ONLY);
		this.downGameAction = new GameAction("move down", GameAction.DETECT_INITIAL_PRESS_ONLY);
		
		
		this.nextLevel = new GameAction("next level", GameAction.DETECT_INITIAL_PRESS_ONLY);
		this.inputManager.mapToKey(nextLevel, KeyEvent.VK_N);
		
		this.inputManager.mapToKey(leftGameAction, KeyEvent.VK_LEFT);
		this.inputManager.mapToKey(rightGameAction, KeyEvent.VK_RIGHT);
		this.inputManager.mapToKey(downGameAction, KeyEvent.VK_DOWN);
		this.inputManager.mapToKey(pauseGameAction, KeyEvent.VK_P);
		this.inputManager.mapToKey(exitGameAction, KeyEvent.VK_ESCAPE);
		this.inputManager.mapToKey(rotateGameAction, KeyEvent.VK_SPACE);
	}

	/* (non-Javadoc)
	 * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
	 */
	public void actionPerformed(ActionEvent e) {
		Object src = e.getSource();
		if (src == playButton) {
			playGameAction.tap();
		} else if (src == pauseButton) {
			pauseGameAction.tap();
		} else if (src == exitButton) {
			exitGameAction.tap();
		} 
	}

	
	/* (non-Javadoc)
	 * @see tx.screens.Screen#update(long)
	 */
	public void update(long elapsedTime) {
		checkInput();

		if (!isPaused) {
			checkGameInput(elapsedTime);
			blocks.update(elapsedTime);
			block.update(elapsedTime);
		}
			
		
	}

	/**
	 * Checks user input for the game.
	 * @param elapsedTime The elapsed time.
	 */
	private void checkGameInput(long elapsedTime) {
		float velocityX = 0;
		
		if (leftGameAction.isPressed()) {
			if (Development.DEBUG_ENABLED) System.out.println(Development.DEBUG_LABEL + "GameScreen.update() elapsed: " + elapsedTime + " " + leftGameAction.getName() + " " + leftGameAction.getAmount());
			  velocityX -= Tetraminoe.X_SPEED;
		} 
		if (rightGameAction.isPressed()) {
			if (Development.DEBUG_ENABLED) System.out.println(Development.DEBUG_LABEL + "GameScreen.update() elapsed: " + elapsedTime + " " + rightGameAction.getName() + " " + rightGameAction.getAmount());
			velocityX += Tetraminoe.X_SPEED;
		}
		
		block.setVelocityX(velocityX);
		
		if (downGameAction.isPressed() && !block.isGoingDown()) {
			if (Development.DEBUG_ENABLED) System.out.println(Development.DEBUG_LABEL + "GameScreen.update() elapsed: " + elapsedTime + " " + downGameAction.getName() + " " + downGameAction.getAmount());
			
			block.goDown();
			
		}
		
		if (rotateGameAction.isPressed()) {
			block.rotate();
		}
		
		if(nextLevel.isPressed()) {
			if (Development.DEBUG_ENABLED) System.out.println(Development.DEBUG_LABEL + "GameScreen.update() elapsed: " + elapsedTime + " " + nextLevel.getName() + " " + downGameAction.getAmount());
			currentLevel++;
			currentBGImage = getBackgroundImage(currentLevel);
		}
		
	}

	/**
	 * Checks the input not related to the game play. 
	 */
	private void checkInput() {
		if (playGameAction.isPressed()) setPaused(false);
		if (pauseGameAction.isPressed()) setPaused(true);
		if (exitGameAction.isPressed()) exit();
	}

	/**
	 * Sets the game paused.
	 * @param paused Whether the game should be paused or not. 
	 */
	private void setPaused(boolean paused) {
		if (Development.DEBUG_ENABLED) System.out.println( Development.DEBUG_LABEL +  "GameScreen.setPaused() " + paused);
		
		this.playPauseButtonSpace.removeAll();
		
		this.isPaused = paused;
		
		if (paused) {
			playPauseButtonSpace.add(this.playButton);
		} else {
			playPauseButtonSpace.add(this.pauseButton);
		}
		
	}

	/**
	 * Exits the current game.
	 */
	private void exit() {
		if (Development.DEBUG_ENABLED) System.out.println(Development.DEBUG_LABEL +  "GameScreen.exit()");
		
		this.titleScreen.init();

		this.stop();
		
		this.nextScreen = titleScreen;
		
	}
	
	
    /* (non-Javadoc)
     * @see tx.screens.Screen#draw(java.awt.Graphics2D)
     */
    public void draw(Graphics2D g) {
    	
    	g.drawImage(this.currentBGImage, 0, 0, null);
    	
    	JFrame frame = screenManager.getFullScreenWindow(); 
    // the layered pane contains things like popups (tooltips, 
    // popup menus) and the content pane. 
    	frame.getLayeredPane().paintComponents(g);
    	
    	block.draw(g);
    }

	
	/* (non-Javadoc)
	 * @see tx.screens.Screen#isRunning()
	 */
	public boolean isRunning() {
		return this.isRunning;
	}

	/* (non-Javadoc)
	 * @see tx.screens.Screen#nextScreen()
	 */
	public Screen nextScreen() {
		return this.nextScreen;
	}

	/* (non-Javadoc)
	 * @see tx.screens.Screen#stop()
	 */
	public void stop() {
		JFrame frame = screenManager.getFullScreenWindow();
		
		Container contentPane = frame.getContentPane();
		
		
		
		playPauseButtonSpace.removeAll();
		
		contentPane.remove(playPauseButtonSpace);
		contentPane.remove(exitButton);
		
		frame.validate();

		this.currentBGImage = null;
		playPauseButtonSpace = null;
		playButton = null;
		pauseButton = null;
		exitButton = null;
		
		
	}
	
}
